home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Apple II Magazines (PO)
/
Nibble Volume 11, No. 04 - 05 - 06 (1990)(MindCraft Publishing)[no boot].zip
/
Nibble Volume 11, No. 04 - 05 - 06 (1990)(MindCraft Publishing)[no boot].po
/
NIBBLE.1990:APR.VOL11.NO04
/
PRODOS.LINKER.S
< prev
next >
Wrap
Text File
|
1996-12-24
|
24KB
|
767 lines
********************************
* PRODOS.LINKER Source Code *
* by Mark Hadley *
* Copyright (c) 1990 *
* MondCraft Publ. Corp. *
* Concord, MA 01742 *
********************************
* MINDCRAFT ASSEMBLER -- converted to Merlin-8
* ProDOS Equates
EXTRNCMD EQU $BE06 ; External cmd JMP instruction
ERROUT EQU $BE09 ; Error handler
XTRNADDR EQU $BE50 ; Start of external cmd handler
XLEN EQU $BE52 ; External cmd name length (-1)
XCNUM EQU $BE53 ; Command # (0 for external)
PBITS EQU $BE54 ; Command parameter bits
FBITS EQU $BE56 ; Parameters found in pares
APARM EQU $BE58 ; Address parameter specified
SPARM EQU $BE61 ; Slot parameter specified
DPARM EQU $BE62 ; Drive parameter specified
GETBUFR EQU $BEF5 ; Get free space
MLI EQU $BF00 ; Machine Language Interface
BITMAP EQU $BF58 ; Protected memory locations
* variables
DESTPTR EQU $00 ; ZP pointer for prgm dest
TSPTR EQU $02 ; ZP pointer for T/S list
DATAPTR EQU $04 ; ZP pointer for data
FLNMLEN EQU $250 ; Length of filename
YTEMP EQU $251 ; Save Y-reg
CMD EQU $252 ; Command (0-DLOAD, 1-DBLOAD)
TRACK EQU $253 ; Track to read from
SECTOR EQU $254 ; Sector to read from
LENGTH EQU $255 ; Length of file
PARMTBL EQU $257 ; Beginning of parameter table
UNITNUM EQU $258 ; Code for slot/drive
BUFFER EQU $259 ; Pointer to buffer to load at
BLOCK EQU $25B ; Block to load from disk
FILENAME EQU $25D ; Name of file to load
* Other equates:
A1L EQU $3C ; Beginning addr of move
A2L EQU $3E ; Ending addr of move
A4L EQU $42 ; Destination of move
TXTTAB EQU $67 ; Start of Applesoft program
VARTAB EQU $69 ; Start of simple variable space
HIMEM EQU $73 ; Start of ProDOS buffer
PRGEND EQU $AF ; End of Applesoft program
IN EQU $200 ; Input buffer
KBD EQU $C000 ; Keypress input
KBSTROBE EQU $C010 ; Key-pressed status
LINPRT EQU $ED24 ; Prints 16 bit hex as decimal
CROUT EQU $FD8E ; Output a carriage return
COUT EQU $FDED ; Output a character in Acc
MOVE EQU $FE2C ; Move block of memory
ORG $2000
* Resetve space for commands
SEC ; Calcuate # pages to reserve
LDA #>END100
SBC #>BEGIN
STA PAGES
JSR GETBUFR ; Open room for commands
BCC INSTALL
LDA #14 ; BASIC error: prgm too large
JMP ERROUT ; Print error msg and quit
INSTALL STA PGSTART ; Addr of freed space
LDA EXTRNCMD+1 ; Must create link to
STA NEXTCMD+1 ; any existing external
LDA EXTRNCMD+2 ; commands
STA NEXTCMD+2
LDA #0 ; Install commands
STA EXTRNCMD+1 ; by storing the command
LDA PGSTART ; handler address at
STA EXTRNCMD+2 ; EXTRNCMD
* Protect memory from ProDOS operations
LDA PGSTART
STA PRTCTPG ; Starting page to protect
PROTECT LDA PRTCTPG ; Get page to protect
JSR SETBIT
INC PRTCTPG
DEC PAGES ; Finished with all pages?
BNE PROTECT ; No, keep going.
* Relocate the command code
LDX #0
LDA PGSTART ; Get the new page number
SEC
SBC #>BEGIN ; Offset to new location
STA OFFSET
LDA #<BEGIN ; Search for cmds to relocate
STA $FE
LDA #>BEGIN
STA $FF
LDY #0
RELOC1 LDA ($FE),Y
CMP #$D9 ; CMP command?
BEQ RELOC3
CMP #$20 ; JSR command?
BEQ RELOC3
CMP #$4C ; JMP command?
BEQ RELOC3
CMP #$B9 ; LDA command?
BEQ RELOC3
CMP #$BD ; LDA,X command?
BEQ RELOC3
INC $FE
BNE RELOC2
INC $FF
RELOC2 LDA $FF
CMP #>END100
BNE RELOC1
JMP RELOC4
RELOC3 CLC
LDA $FE
ADC #2
STA $FE
LDA $FF
ADC #0
STA $FF
LDA ($FE),Y
CMP #>BEGIN ; Within bounds of program?
BCC RELOC2 ; No, so don't change
CMP #>END100
BCS RELOC2 ; No, so don't change
ADC OFFSET ; Yes, change it
STA ($FE,X)
JMP RELOC2 ; and continue
RELOC4 LDA #>BEGIN
STA $FF
LDA #<BEGIN
STA $FE
RELOC5 LDA ($FE),Y
CMP #$A9 ; LDA command?
BEQ RELOC7
RELOC6 INC $FE
BNE RELOC5
BEQ RELOC8
RELOC7 INC $FE
LDA ($FE),Y
CMP #>BEGIN ; First page of program?
BCC RELOC6 ; No, don't change it
CMP #>END100 ; Less than last page?
BCS RELOC7 ; No, don't change it
ADC OFFSET ; Yes, change to new place
STA ($FE),Y
JMP RELOC6 ; and continue looking...
RELOC8 EQU *
* Move the code to final position
LDA #<BEGIN
STA A1L
LDA #>BEGIN
STA A1L+1
LDA #<END
STA A2L
LDA #>END
STA A2L+1
LDA #0
STA A4L
LDA PGSTART
STA A4L+1
LDY #0
JSR MOVE ; Move the code and exit
* Announce successful installation
* and display commands and parameters
LDY #0
MSGOUT LDA MSG,Y ; Get next character
JSR COUT ; and print it
INY
CPY #MSGEND-MSG ; Finished?
BNE MSGOUT ; Nope, keep going.
RTS ; Yes, installation complete
SETBIT PHA ; Acc holds high addr byte
AND #$07 ; of memory page to protect
TAX
PLA
LSR ; Upper five bits of Acc
LSR ; make an index into
LSR ; the bit map
TAY
LDA #0 ; Lower 3 bits of Acc
SEC ; choose the bit to
SETLP ROR ; be set. X-reg counts
DEX ; while bit is shifted into
BPL SETLP ; place from carry
ORA BITMAP,Y
STA BITMAP,Y ; Set the bit in bit map
RTS
PAGES DS 1 ; Length of command handler
PGSTART DS 1 ; Start of command handler
OFFSET DS 1 ; Offset to new location
PRTCTPG DS 1 ; Next page to protect
MSG HEX 8D8D
ASC "ProDOS Linker Installed"
HEX 8D
ASC "COMMANDS:"
HEX 8D
ASC "DLOAD <NAME>[,S#][,D#]"
HEX 8D
ASC "DBLOAD <NAME>[,S#][,D#][,A ADDR]"
HEX 8D
ASC "DCAT [,S#][,D#]"
HEX 8D8D
MSGEND EQU *
DS $2200-* ; (Fill 0's to page boundary)
BEGIN EQU *
********************************
* Check for commands
CLD ; Req'd as I.D. for external cmd
LDY #0
LDX #0
CHKCMD LDA IN,X ; Get command character
INX
CMP #$A0 ; Is it a blank?
BEQ CHKCMD ; If it is, ignore it
CMP DLNAME,Y ; Same as DLOAD?
BEQ CHKCMD1 ; Yes, continue to end
CMP #$E0 ; Lower case?
BCC NOTFOUND1 ; No, so try next
AND #$DF ; Yes, convert to uppercase
CMP DLNAME,Y ; OK now?
BNE NOTFOUND1 ; No, so try next
CHKCMD1 INY
CPY #DLLEN-DLNAME ; At end?
BNE CHKCMD ; No, so continue
BEQ DLOAD ; Yes, so DLOAD requested
NOTFOUND1 LDY #0 ; Try next command
LDX #0
CHKCMD2 LDA IN,X
INX
CMP #$A0
BEQ CHKCMD2
CMP DBNAME,Y
BEQ CHKCMD3
CMP #$E0
BCC NOTFOUND2
AND #$DF
CMP DBNAME,Y
BNE NOTFOUND2
CHKCMD3 INY
CPY #DBLEN-DBNAME
BNE CHKCMD2
BEQ DBLOAD ; DBLOAD requested
NOTFOUND2 LDY #0
LDX #0
CHKCMD4 LDA IN,X ; Get command character
INX
CMP #$A0 ; Is it a blank?
BEQ CHKCMD4 ; If it is, ignore it
CMP DCNAME,Y ; Save as DCAT?
BEQ CHKCMD5 ; Yes, continue to end
CMP #$E0 ; Lower case?
BCC NOTFOUND3 ; No, so exit
AND #$DF ; Yes, convert to uppercase
CMP DCNAME,Y ; OK now?
BNE NOTFOUND3 ; No, so exit
CHKCMD5 INY
CPY #DCLEN-DCNAME ; At end?
BNE CHKCMD4 ; No, so continue
BEQ DCAT ; Yes, so DCAT requested
NOTFOUND3 SEC ; Set carry to indicate failure
NEXTCMD JMP $0000 ; Jump to next external cmd
DLOAD JSR GETNAME ; Get filename after cmd
LDA #$00
STA XCNUM ; Extername cmd number = 0
LDA #<EXECUTE ; Store return address
STA XTRNADDR ; for BASIC.SYSTEM
LDA #>EXECUTE
STA XTRNADDR+1
* Set up parsing rules for DLOAD
LDA #$10 ; Pathname is not required
STA PBITS
LDA #$04 ; Allow slot and drive parms
STA PBITS+1
LDA #$00
STA CMD ; DLOAD command active
CLC ; Command recognized
RTS ; Let BASIC.SYSTEM do parsing
DBLOAD JSR GETNAME ; Get filename after cmd
LDA #0
STA XCNUM ; Externale cmd number = 0
LDA #<EXECUTE ; Set return address
STA XTRNADDR ; for BASIC SYSTEM
LDA #>EXECUTE
STA XTRNADDR+1
* Set up parsing rules
LDA #$10 ; Pathname not required
STA PBITS
LDA #$84 ; Address, Slot, Drive parms
STA PBITS+1 ; allowed
LDA #$01
STA CMD ; DBLOAD command active
CLC ; Command recognized
RTS ; Let BASIC.SYSTEM do parsing
DCAT DEY ; Length of command
STY XLEN
LDA #0
STA XCNUM ; External cmd number = 0
LDA #<EXECAT ; Set return address
STA XTRNADDR ; for BASIC.SYSTEM
LDA #>EXECAT
STA XTRNADDR+1
* Set parsing rules for DCAT
LDA #$10 ; Pathname not required
STA PBITS
LDA #$04 ; Slot, Drive parms allowed
STA PBITS+1
CLC ; Command recognized
RTS ; Let BASIC.SYSTEM do parsing
* Find filename in DIS 3.3 directory
EXECUTE JSR SETPARMS ; Set up MLI parameters
JSR CHECKPRO ; Check for ProDOS disk
LDA #3 ; Set # parms for READ_BLOCK
STA PARMTBL
LDA #$11 ; Track containing DOS directory
STA TRACK
LDA #$0F ; Sector of directory start
STA SECTOR
NEXTDIR CLC ; Load in data buffer
JSR READTS ; Get start of directory
LDA #$0B ; Position of first entry
STA DATAPTR
NXTENTRY LDY #0 ; Track of t/x list
LDA (DATAPTR),Y
CMP #$FF ; If file is deleted, skip it
BEQ NOTFILE
LDY #3 ; Start of filename
LDX #0
CHKFLNM LDA (DATAPTR),Y
CMP FILENAME,X
BNE NOTFILE
INY
INX
CPX FLNMLEN
BCC CHKFLNM
CPX #30 ; If filename 30 chars ling
BEQ GOTFILE ; then file is correct one
LDA (DATAPTR),Y ; If next character is space,
CMP #$A0 ; then file is correct one
BEQ GOTFILE
NOTFILE CLC
LDA DATAPTR
ADC #$23 ; Move to next entry
STA DATAPTR
BCC NXTENTRY
LDY #0 ; Must get next sector
LDA #1
STA DATAPTR
LDA (DATAPTR),Y ; Track of next directory
BEQ FLNOTFND ; If 0, then no such file
STA TRACK
INY
LDA (DATAPTR),Y ; Sector of next directory
STA SECTOR
BPL NEXTDIR ; Always branch
FLNOTFND LDA #7 ; BASIC error: path not found
SEC ; Flag error
RTS ; Let BASIC.SYSTEM report error
* File found, make sure it si right type
GOTFILE LDY #2 ; Location of file type
LDA (DATAPTR),Y
AND #$0F ; Remove locked indicator
LDX CMD ; Looking for Applesoft file?
BEQ CHKAPLS ; Yes, so branch
CMP #4 ; check for Binary file
BEQ TYPEOK ; File is correct type
BADTYPE LDA #$0D ; BASIC err: file type mismatch
SEC ; Flag error
RTS ; Let BASIC.SYSTEM report error
CHKAPLS CMP #2 ; Check for Applesoft file
BNE BADTYPE ; Error if not right
TYPEOK EQU * ; Type is what we're looking for
* Find and load file's T/S sector
LDY #0 ; Track of file's T/S
LDA (DATAPTR),Y
STA TRACK
INY ; Sector of file's T/S
LDA (DATAPTR),Y
STA SECTOR
SEC ; Load into T/S buffer
JSR READTS
* Load file's first data sector
LDY #$0C ; Track of first file sector
LDA (TSPTR),Y
STA TRACK
INY ; Sector of first file sector
LDA (TSPTR),Y
STA SECTOR
INY
STY YTEMP
CLC ; Load to data buffer
JSR READTS ; Get the first sector
LDA CMD ; Working on Applesoft file?
BEQ APLSOFT ; Yes, so branch
LDY #0 ; Find where file goes
LDA (DATAPTR),Y
STA DESTPTR
INY
LDA (DATAPTR),Y
STA DESTPTR+1
LDA FBITS+1 ; Now check if addr specified
BPL ADDROK ; Not specified
LDA APARM ; Specified, so get it
STA DESTPTR
LDA APARM+1
STA DESTPTR+1
ADDROK INY ; Location of file length
LDA (DATAPTR),Y
STA LENGTH
INY
LDA (DATAPTR),Y
STA LENGTH+1
INY
JMP LOADFILE ; Now go move file into memory
APLSOFT LDA TXTTAB ; Start of Applesoft program
STA DESTPTR
LDA TXTTAB+1
STA DESTPTR+1
LDY #0 ; Location of file length
LDA (DATAPTR),Y
STA LENGTH
INY
LDA (DATAPTR),Y
STA LENGTH+1
INY
* Load the file into memory
LOADFILE LDX #0 ; No pre-index needed
LDA (DATAPTR),Y ; Get byte of file
STA (DESTPTR,X) ; Store it in memory
INC DESTPTR
BNE NOINC
INC DESTPTR+1
NOINC SEC ; For subtraction
LDA LENGTH
SBC #1
STA LENGTH
LDA LENGTH+1
SBC #0
STA LENGTH+1
BNE CONT ; Check if LENGTH = 0
LDA LENGTH
BEQ DONE ; Finished loading file!
CONT INY ; Move to next sector?
BNE LOADFILE ; Still in same sector
LDY YTEMP ; Get back T/S index
LDA (TSPTR),Y ; Next track
STA TRACK
INY
LDA (TSPTR),Y ; Next sector
STA SECTOR
INY
STY YTEMP
CLC ; Load into data buffer
JSR READTS ; Get next sector
LDY YTEMP ; Should next T/S be loaded?
BNE SAMETS ; No, keep this T/S list
LDY #2 ; Yes, get linking track, sector
LDA (TSPTR),Y ; Link track
STA TRACK
INY
LDA (TSPTR),Y ; Link sector
STA SECTOR
LDY #$0C ; Next file sector
STY YTEMP
SEC ; Load into T/S buffer
JSR READTS ; Get next T/S list
SAMETS LDY #0
JMP LOADFILE ; Continue loading file
* Done loading! Finish up Applesoft load.
DONE LDA CMD
BNE GOODEXIT ; DBLOAD, so all is done
LDA DESTPTR ; Tell Applesoft where prgm ends
STA PRGEND
LDA DESTPTR+1
STA PRGEND+1
INC DESTPTR
BNE NOINC2
INC DESTPTR+1
NOINC2 LDA DESTPTR ; Also set beginning of simple
STA VARTAB ; variable space at end of
LDA DESTPTR+1 ; program
STA VARTAB+1
GOODEXIT CLC ; No errors
RTS ; Return to BASIC.SYSTEM
* Catalog DOS 3.3 disk
EXECAT JSR SETPARMS ; Set up MLI parameters
JSR CHECKPRO ; Check for ProDOS disk
LDA #3 ; Set # parms for READ_BLOCK
STA PARMTBL
JSR CROUT
LDA #$11 ; Track of directory
STA TRACK
LDA #$0F ; Sector of directory
STA SECTOR
LDA #0 ; To keep track of # listed,
STA DESTPTR ; DESTPTR temp. used
NEXTSEC CLC ; Load into DATA buffer
JSR READTS
LDA #$0B ; First entry in directory
STA DATAPTR
NXTFILE LDY #0 ; Track of file data
LDA (DATAPTR),Y ; = $FF if deleted
BNE CHKDEL ; Not end of directory
LDY #3
LDA (DATAPTR),Y
BEQ DONECAT ; If first name chr=0, then done
CHKDEL CMP #$FF ; Is file deleted?
BEQ SKIPDEL ; Don't list if deleted
LDX #" " ; Assume file unlocked
LDY #2 ; Position of file type, lock
LDA (DATAPTR),Y ; Is file locked?
BPL NOLOCK ; No, print space
LDX #"*" ; Yes, print asterisk
NOLOCK TXA
JSR COUT ; Print lock status
LDA (DATAPTR),Y
AND #$0F ; Ignore lock, unknown types
TAX
LDA TYPESYMB,X ; Symbol to display (T,I,A,B)
JSR COUT
LDA #" "
JSR COUT
LDY #$21 ; Position of file size (sectors)
LDA (DATAPTR),Y
JSR PRNTSIZE ; Convert to decimal and display
LDA #" "
JSR COUT
LDY #3 ; Beginning of file name
PRNTNAME LDA (DATAPTR),Y ; Get character of name
BEQ DONENAME ; If zero, then name is finished
JSR COUT ; Print the character
INY
CPY #$21 ; Max filename length 30 characters
BCC PRNTNAME
DONENAME JSR CROUT
INC DESTPTR ; Check if 20 names listed
LDA DESTPTR
CMP #20
BNE SKIPDEL ; Continue of not 20
CHKEY LDA KBD ; Wait for keypress
BPL CHKEY ; No keypress
BIT KBSTROBE ; Clear key pressed status
CMP #$83 ; CTRL-C?
BEQ DONECAT ; Yes, halt catalog
LDA #0 ; Reset # cataloged to 0
STA DESTPTR
SKIPDEL CLC
LDA DATAPTR ; Move to next entry
ADC #$23
STA DATAPTR
BCC NXTFILE ; Continue in this directory
LDY #1 ; Track of next directory
LDA (DATAPTR),Y
STA TRACK
INY ; Sector of next directory
LDA (DATAPTR),Y
BEQ DONECAT ; If zero, then CAT is done
STA SECTOR
JMP NEXTSEC
* Done with DCAT! Clean up the mess...
DONECAT JSR CROUT
CLC ; No errors
RTS ; Ride off into the sunset
* Find filename and save it
GETNAME LDA IN,X ; Get next character
CMP #$A0 ; Is it blank?
BNE GTNM2 ; No, part of file name
INX
BNE GETNAME ; Always branch
GTNM2 LDY #0
GTNM3 LDA IN,X ; Get character
CMP #$8D ; End of command?
BEQ GOTNAME
CMP #"," ; End of file name?
BEQ GOTNAME
STA FILENAME,Y
INY
INX
BNE GTNM3 ; Always branch
GOTNAME STY FLNMLEN ; Store length of file name
DEX ; Save command + name length so
STX XLEN ; ProDOS ignores DOS filename
RTS
* Convert track/sector into block and load it
READTS PHP ; Save carry flag for later
LDA HIMEM ; Find buffer to load to
STA BUFFER
LDA HIMEM+1
BCS TSBUF ; Skip next if for T/S buffer
ADC #2 ; Set buffer to DATA buffer
TSBUF STA BUFFER+1
LDA TRACK
ASL ; Multiply track # by 8
ASL
ASL
STA BLOCK
LDA #0
STA BLOCK+1
ROL BLOCK+1 ; Shift carry bit into BLOCK+1
LDY SECTOR ; Find relative block number
LDA RELBLOCK,Y ; containing desired sector
CLC
ADC BLOCK ; Add relative block to base
STA BLOCK
JSR MLI
DFB $80 ; READ_BLOCK
DA PARMTBL
BCS READERR ; Bad news if carry set
LDY SECTOR ; Find half of block to read
LDA BLKHALF,Y ; contains sector
PLP ; Recover carry flag
BCC DTBUF ; If clear, set up DATAPTR
CLC
ADC BUFFER+1
STA TSPTR+1
LDA #0
STA TSPTR
BEQ READDONE
DTBUF ADC BUFFER+1
STA DATAPTR+1
LDA #0
STA DATAPTR
READDONE RTS
READERR PLA ; Pop return addr, status
PLA
PLA
LDA #8 ; Other erros reported as
SEC ; BASIC.SYSTEM I/O ERROR
RTS
* Set parameters for READ_BLOCK command
SETPARMS LDA SPARM ; Get slot specified
ASL ; Slot * 16
ASL
ASL
ASL
LDX DPARM ; Get drive specified
CPX #2 ; Drive 2?
BNE SAVEUNIT
ORA #$80 ; Set bit for drive 2
SAVEUNIT STA UNITNUM ; Store slot/drive as unit #
RTS
* Make sure disk is not ProDOS disk
CHECKPRO LDA #2 ; Set # parms for ON_LINE
STA PARMTBL
LDA #<IN
STA BUFFER
LDA #>IN
STA BUFFER+1
JSR MLI ; Do ON_LINE command
DFB $C5 ; Code for ON_LINE
DA PARMTBL
CMP #$52 ; MLI error: not ProDOS disk
BEQ NOPRO ; That's what we want!
TAX ; Save error code in X
PLA ; Pop off return addr from stack
PLA
LDA #3 ; Assume error; no device
CPX #$28 ; MLI error: no device
BEQ SKIPIO ; If no device, don't print I/O
LDA #8 ; Other error, flag as I/O error
SKIPIO SEC
NOPRO RTS ; Return to BASIC or command
* Convert filesize to decimal and display it
PRNTSIZE TAX ; Copy filesize to X-reg
LDA #"0" ; Load Acc with ASCII 0
CPX #100 ; Is it greater than 100?
BCS NOZERO ; Yes, don't print leading zeros
CPX #10 ; Is it greater than 10?
BCS ONEZERO ; Yes, only one leading zero
JSR COUT ; Print 1 of 2 leading zeros
ONEZERO JSR COUT ; Print a leading 0
* LINPRT converts hex number in X-reg (low) and
* Acc (high) into decimal number and displays it,
* therefore Acc must be 0 and X-reg is filesize
NOZERO LDA #0
JMP LINPRT ; Display filesize
* relative block numbers for sector computations
RELBLOCK DFB 0,7,6,6,5,5,4,4
DFB 3,3,2,2,1,1,0,7
* Locate half of block containing sector
BLKHALF DFB 0,0,1,0,1,0,1,0
DFB 1,0,1,0,1,0,1,1
* Filetype symbols
TYPESYMB ASC "TIA B " ; 11 spaces after B
* Command names
DLNAME ASC "DLOAD"
DLLEN EQU *
DBNAME ASC "DBLOAD"
DBLEN EQU *
DCNAME ASC "DCAT"
DCLEN EQU *
END EQU *
END100 EQU END+$100 ; For relocating routine